package com.sinog.biz.util;

import com.sinog.core.model.UploadAppendixFile;
import com.sinog.core.util.JsonUtil;
import com.sinog.core.utilbean.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @desc 文件上传下载
 * @author hak
 * @date 2019年11月26日
 */
@Slf4j
@Component
public final class FileUtil {

    private static String uploadFilePath;
    private static String linuxUploadFilePath;
    private static String datapath;
    /**
     * 湖南看守所对接用到的路径
     */
    private static String localpath;
    private static String unixlocalpath;
    private static String xmlpathTemp;
    private static String wsPath;
    private static String sacwPath;
    private static String jzPath;
    private static String zipPath;
    private static String unixxmlpathTemp;
    private static String unixwsPath;
    private static String unixsacwPath;
    private static String unixjzPath;
    private static String unixzipPath;
    private static String localpathTwo;
    private static String unixlLocalpathTwo;

    @Value("${gkzx.azbj.uploadfilepath.windows}")
    public void setUploadFilePath(String uploadFilePath) {
        FileUtil.uploadFilePath = uploadFilePath;
    }

    @Value("${gkzx.azbj.uploadfilepath.unix}")
    public void setLinuxUploadFilePath(String linuxUploadFilePath) {
        FileUtil.linuxUploadFilePath = linuxUploadFilePath;
    }

    @Value("${gkzx.azbj.ftp.datapath}")
    public void setDatapath(String datapath) {
        FileUtil.datapath = datapath;
    }

    /**
     * 湖南看守所对接 - zip包下载到本地服务器的路径
     * @param localpath localpath
     */
    @Value("${gkzx.azbj.zip.pull.localpath}")
    private void setLocalPath(String localpath) {
        FileUtil.localpath = localpath;
    }

    /**
     * unix 湖南看守所对接 - zip包下载到本地服务器的路径
     * @param unixlocalpath unixlocalpath
     */
    @Value("${gkzx.azbj.zip.pull.unixlocalpath}")
    private void setUnixlocalpath(String unixlocalpath) {
        FileUtil.unixlocalpath = unixlocalpath;
    }

    /**
     * xml组装后存放的临时路径
     * @param xmlpathTemp xmlpathTemp
     */
    @Value("${gkzx.azbj.zip.push.xmlpathTemp}")
    private void setXmlpathTemp(String xmlpathTemp) {
        FileUtil.xmlpathTemp = xmlpathTemp;
    }

    /**
     * 文书空目录
     * @param wsPath wsPath
     */
    @Value("${gkzx.azbj.zip.push.wsPath}")
    private void setWsPath(String wsPath) {
        FileUtil.wsPath = wsPath;
    }

    /**
     * sacw空目录
     * @param sacwPath sacwPath
     */
    @Value("${gkzx.azbj.zip.push.sacwPath}")
    private void setSacwPath(String sacwPath) {
        FileUtil.sacwPath = sacwPath;
    }

    /**
     * jz空目錄
     * @param jzPath jzPath
     */
    @Value("${gkzx.azbj.zip.push.jzPath}")
    private void setJzPath(String jzPath) {
        FileUtil.jzPath = jzPath;
    }

    /**
     * zip空目录
     * @param zipPath zipPath
     */
    @Value("${gkzx.azbj.zip.push.zipPath}")
    private void setZipPath(String zipPath) {
        FileUtil.zipPath = zipPath;
    }

    /**
     * xml组装后存放的临时路径
     * @param unixxmlpathTemp unixxmlpathTemp
     */
    @Value("${gkzx.azbj.zip.push.unixxmlpathTemp}")
    private void setUnixxmlpathTemp(String unixxmlpathTemp) {
        FileUtil.unixxmlpathTemp = unixxmlpathTemp;
    }

    /**
     * 文书空目录
     * @param unixwsPath unixwsPath
     */
    @Value("${gkzx.azbj.zip.push.unixwsPath}")
    private void setUnixwsPath(String unixwsPath) {
        FileUtil.unixwsPath = unixwsPath;
    }

    /**
     * sacw空目录
     * @param unixsacwPath unixsacwPath
     */
    @Value("${gkzx.azbj.zip.push.unixsacwPath}")
    private void setUnixsacwPath(String unixsacwPath) {
        FileUtil.unixsacwPath = unixsacwPath;
    }

    /**
     * jz空目錄
     * @param unixjzPath unixjzPath
     */
    @Value("${gkzx.azbj.zip.push.unixjzPath}")
    private void setUnixjzPath(String unixjzPath) {
        FileUtil.unixjzPath = unixjzPath;
    }

    /**
     * 本地服务器解压zip包的路径
     * @param unixzipPath unixzipPath
     */
    @Value("${gkzx.azbj.zip.push.unixjzPath}")
    private void setUnixzipPath(String unixzipPath) {
        FileUtil.unixzipPath = unixzipPath;
    }

    @Value("${gkzx.azbj.zip.pull.localpathTwo}")
    private void setLocalpathTwo(String localpathTwo) {
        FileUtil.localpathTwo = localpathTwo;
    }

    @Value("${gkzx.azbj.zip.pull.unixlocalpathTwo}")
    private void setUnixlLocalpathTwo(String unixlLocalpathTwo) {
        FileUtil.unixlLocalpathTwo = unixlLocalpathTwo;
    }

    /**
     * 获取文件上传路径
     * @return String
     */
    public static String getUploadFilePath() {
        String path = uploadFilePath;
        String os = System.getProperty("os.name");
        if(!os.toLowerCase().startsWith(Constants.StrFinalValue.WIN)) {
            path = linuxUploadFilePath;
        }
        return path;
    }

    /**
     * 下载单个文件
     * @param request request
     * @param resp resp
     * @param filePath filePath
     * @param filename filename
     */
    public static void downloadFile(HttpServletRequest request,HttpServletResponse resp,String filePath,String filename) throws IOException {
        log.info("下载单个文件路径:filePath:{}",filePath);
        //获取浏览器
        String userAgent = request.getHeader("User-Agent");
        if(userAgent.contains(Constants.StrFinalValue.MSIE) || userAgent.contains(Constants.StrFinalValue.TRIDENT)) {
            //IE浏览器处理
            filename = URLEncoder.encode(filename,"UTF-8");
        } else {
            // 非IE浏览器的处理
            filename = new String(filename.getBytes(StandardCharsets.UTF_8),StandardCharsets.ISO_8859_1);
        }
        File file = new File(filePath);
        FTPClient ftp;
        //判断文件父目录是否存在
        if(file.exists()) {
            resp.setContentType("application/force-download");
            resp.setHeader("Content-Disposition","attachment;filename=" + filename);
            resp.setContentLength((int)file.length());
            byte[] buffer = new byte[1024];
            //文件输入流
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            OutputStream os = null;
            try {
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                os = resp.getOutputStream();
                int len;
                while(-1 != (len = bis.read(buffer))) {
                    os.write(buffer,0,len);
                }
                resp.flushBuffer();
            } catch(FileNotFoundException e) {
                log.error("文件未找到:",e);
            } catch(IOException e) {
                log.error("发生异常:",e);
            } finally {
                try {
                    if(null != bis) {
                        bis.close();
                    }
                } catch(IOException e) {
                    log.error("发生异常:",e);
                }
                try {
                    if(null != os) {
                        os.close();
                    }
                } catch(IOException e) {
                    log.error("发生异常:",e);
                }
                try {
                    if(null != fis) {
                        fis.close();
                    }
                } catch(IOException e) {
                    log.error("发生异常:",e);
                }
            }
        } else {
            String os = System.getProperty("os.name");
            if(os.toLowerCase().startsWith(Constants.StrFinalValue.WIN)) {
                filePath = filePath.replace("/","\\");
            } else {
                filePath = filePath.replace("\\","/");
            }
            String[] fileUrlArr = filePath.split(Matcher.quoteReplacement(File.separator));
            String ftppathTemp = filePath.substring(filePath.indexOf(File.separator),filePath.lastIndexOf(File.separator));
            String ftppath;
            if(os.toLowerCase().startsWith(Constants.StrFinalValue.WIN)) {
                ftppath = datapath + ftppathTemp.substring(ftppathTemp.indexOf(File.separator,ftppathTemp.indexOf('\\') + 1));
            } else {
                ftppath = datapath + ftppathTemp.substring(ftppathTemp.indexOf(File.separator,ftppathTemp.lastIndexOf('/')) + 1);
            }
            String localDirPath = filePath.substring(0,filePath.lastIndexOf(File.separator));
            PrintWriter writer = null;
            try {
                ftp = FtpUtil.getFtpClient();
                FTPFile[] ftpFiles = ftp.listFiles(ftppath);
                boolean ftpExist = FtpUtil.isFileExist(fileUrlArr[fileUrlArr.length - 1],ftpFiles);
                if(ftpExist) {
                    boolean copyFileFlag = FtpUtil.downloadFile(ftp,ftppath,fileUrlArr[fileUrlArr.length - 1],localDirPath);
                    if(copyFileFlag) {
                        file = new File(filePath);
                        resp.setContentType("application/force-download");
                        resp.setHeader("Content-Disposition","attachment;filename=" + filename);
                        resp.setContentLength((int)file.length());
                        byte[] buffer = new byte[1024];
                        //文件输入流
                        FileInputStream fis = null;
                        BufferedInputStream bis = null;
                        OutputStream os1 = null;
                        try {
                            fis = new FileInputStream(file);
                            bis = new BufferedInputStream(fis);
                            os1 = resp.getOutputStream();
                            int len;
                            while((len = bis.read(buffer)) != -1) {
                                os1.write(buffer,0,len);
                            }
                            resp.flushBuffer();
                        } catch(IOException e) {
                            log.error("发生异常:",e);
                        } finally {
                            try {
                                if(null != bis) {
                                    bis.close();
                                }
                            } catch(IOException e) {
                                log.error("发生异常:",e);
                            }
                            try {
                                if(null != os1) {
                                    os1.close();
                                }
                            } catch(IOException e) {
                                log.error("发生异常:",e);
                            }
                            try {
                                if(null != fis) {
                                    fis.close();
                                }
                            } catch(IOException e) {
                                log.error("发生异常:",e);
                            }
                        }
                    } else {
                        try {
                            resp.setContentType("text/html; charset=utf-8");
                            writer = resp.getWriter();
                            writer.println("<script type=\"text/javascript\">alert('文件下载失败，请重试');window.history.back(-1);</script>");
                            writer.flush();
                        } catch(IOException e) {
                            log.error("发生异常:",e);
                        } finally {
                            if(null != writer) {
                                writer.close();
                            }
                        }
                    }
                } else {
                    try {
                        resp.setContentType("text/html; charset=utf-8");
                        writer = resp.getWriter();
                        writer.println("<script type=\"text/javascript\">alert('文件不存在');window.history.back(-1);</script>");
                        writer.flush();
                    } catch(IOException e) {
                        resp.setContentType("text/html; charset=utf-8");
                        writer = resp.getWriter();
                        writer.println("<script type=\"text/javascript\">alert('文件不存在');window.history.back(-1);</script>");
                        writer.flush();
                        log.error("发生异常:",e);
                    } finally {
                        if(null != writer) {
                            writer.close();
                        }
                    }
                }
            } catch(RuntimeException e) {
                resp.setContentType("text/html; charset=utf-8");
                writer = resp.getWriter();
                writer.println("<script type=\"text/javascript\">alert('文件不存在');window.history.back(-1);</script>");
                writer.flush();
                log.error("发生异常:",e);
            }
        }
    }

    /**
     * 批量下载 -mysql版本
     * @param response response
     * @param request request
     * @param list list
     * @param path path
     * @param datapath datapath
     * @return String
     */
    public static String downloadAppendixAllFile(HttpServletResponse response,HttpServletRequest request,List<UploadAppendixFile> list,String path,String datapath) throws IOException {
        log.error("下载下拉文件开始,下载省文件:下载地址{}",datapath);
        //压缩文件初始设置
        String account = list.get(0).getAccount();
        // 拼接zip文件
        String fileZip = account + ".zip";
        //之后用来生成zip文件
        String filePath = path + fileZip;
        // 创建临时压缩文件
        BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(filePath)));
        ZipOutputStream zos = new ZipOutputStream(bos);
        BufferedInputStream bis = null;
        FTPClient ftp = null;
        try {
            for(UploadAppendixFile uploadAppendixFile : list) {
                String filepath = path + uploadAppendixFile.getFileUrl();
                File localfile = new File(filepath);
                if(!localfile.exists()) {
                    String[] fileUrl = uploadAppendixFile.getFileUrl().split("\\\\");
                    if(uploadAppendixFile.getFileUrl().contains("/")) {
                        fileUrl = uploadAppendixFile.getFileUrl().split("/");
                    }
                    //从ftp下载到本地
                    ftp = FtpUtil.getFtpClient();
                    FtpUtil.downloadFile(ftp,datapath + fileUrl[0],fileUrl[1],path + fileUrl[0]);
                }
                bis = new BufferedInputStream(new FileInputStream(localfile));
                ZipEntry ze = new ZipEntry(uploadAppendixFile.getFileName());
                zos.putNextEntry(ze);
                int s;
                while(-1 != (s = bis.read())) {
                    zos.write(s);
                }
            }
        } catch(FileNotFoundException e) {
            log.error(">>>>>>>>>>文件{}不存在<<<<<<<<<<",e.getMessage());
        } catch(IOException e) {
            log.error("发生异常:",e);
        } finally {
            zos.flush();
            zos.close();
            Objects.requireNonNull(bis).close();
            FtpUtil.closeFtpClient(ftp);
        }
        // 将路径信息,zip文件名信息封装map返回。
        HashMap<String,Object> hashMap = new HashMap<>(16);
        hashMap.put("filePath",filePath);
        hashMap.put("fileZip",fileZip);
        return JsonUtil.toText(hashMap);
    }

    /**
     * 递归压缩方法
     * @param sourceFile 源文件
     * @param zos zip输出流
     * @param name 压缩后的名称
     * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构; false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
     */
    private static void compress(File sourceFile,ZipOutputStream zos,String name,boolean keepDirStructure) throws Exception {
        byte[] buf = new byte[1024];
        if(sourceFile.isFile()) {
            // 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
            zos.putNextEntry(new ZipEntry(name));
            // copy文件到zip输出流中
            int len;
            try(FileInputStream in = new FileInputStream(sourceFile)) {
                while((len = in.read(buf)) != -1) {
                    zos.write(buf,0,len);
                }
                zos.closeEntry();
            } catch(Exception e) {
                log.error("发生异常:",e);
            }
        } else {
            //是文件夹
            File[] listFiles = sourceFile.listFiles();
            if(null == listFiles || 0 == listFiles.length) {
                // 需要保留原来的文件结构时,需要对空文件夹进行处理
                if(keepDirStructure) {
                    // 空文件夹的处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    // 没有文件，不需要文件的copy
                    zos.closeEntry();
                }
            } else {
                for(File file : listFiles) {
                    // 判断是否需要保留原来的文件结构
                    if(keepDirStructure) {
                        // 注意：file.getName()前面需要带上父文件夹的名字加一斜杠,
                        // 不然最后压缩包中就不能保留原来的文件结构,即：所有文件都跑到压缩包根目录下了
                        compress(file,zos,name + "/" + file.getName(),keepDirStructure);
                    } else {
                        compress(file,zos,file.getName(),keepDirStructure);
                    }
                }
            }
        }
    }

    /**
     * 将文件从ftp下载到本地
     * @param response response
     * @param request request
     * @param uploadAppendixFile uploadAppendixFile
     * @param path path
     * @param datapath datapath
     */
    public static void downloadFile(HttpServletResponse response,HttpServletRequest request,UploadAppendixFile uploadAppendixFile,String path,String datapath) throws IOException {
        FTPClient ftp = null;
        try {
            String[] file = uploadAppendixFile.getFileUrl().split("/");
            String filepath = path + file[1];
            log.info("要下载到本地的目录文件路径:{}",filepath);
            File localfile = new File(filepath);
            if(!localfile.exists()) {
                String[] fileUrl = uploadAppendixFile.getFileUrl().split("\\\\");
                if(uploadAppendixFile.getFileUrl().contains(Constants.SpecialChar.NUM_CHAR_G)) {
                    fileUrl = uploadAppendixFile.getFileUrl().split("/");
                }
                //从ftp下载到本地
                ftp = FtpUtil.getFtpClient();
                FtpUtil.downloadFile(ftp,datapath + fileUrl[0],file[1],path);
            }
            downloadFile(request,response,filepath,file[1]);
        } finally {
            FtpUtil.closeFtpClient(ftp);
        }
    }

    /**
     * 湖南看守所个性化对接配置
     * @return Map<String,String>
     */
    public static Map<String,String> getFilePathDetails() {
        HashMap<String,String> detailsMap = new HashMap<>(16);
        String os = System.getProperty("os.name");
        if(!os.toLowerCase().startsWith(Constants.StrFinalValue.WIN)) {
            detailsMap.put("localpath",unixlocalpath);
            detailsMap.put("xmlpathTemp",unixxmlpathTemp);
            detailsMap.put("wsPath",unixwsPath);
            detailsMap.put("sacwPath",unixsacwPath);
            detailsMap.put("jzPath",unixjzPath);
            detailsMap.put("zipPath",unixzipPath);
            detailsMap.put("localpathTwo",unixlLocalpathTwo);
            return detailsMap;
        }
        detailsMap.put("localpath",localpath);
        detailsMap.put("xmlpathTemp",xmlpathTemp);
        detailsMap.put("wsPath",wsPath);
        detailsMap.put("sacwPath",sacwPath);
        detailsMap.put("jzPath",jzPath);
        detailsMap.put("zipPath",zipPath);
        detailsMap.put("localpathTwo",localpathTwo);
        return detailsMap;
    }

    /**
     * file文件拷贝
     * @param oldFile 源文件
     * @param newFile 目标文件
     */
    public static void copyFile(File oldFile,File newFile) {
        try(InputStream in = Files.newInputStream(oldFile.toPath());
            OutputStream out = Files.newOutputStream(newFile.toPath())) {
            byte[] arr = new byte[1024];
            int len;
            while((len = in.read(arr)) != -1) {
                out.write(arr,0,len);
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

}